{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pickle\n",
    "import numpy as np\n",
    "import tensorflow as tf \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 原始数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training set (200000, 28, 28) (200000,)\n",
      "Validation set (10000, 28, 28) (10000,)\n",
      "Test set (10000, 28, 28) (10000,)\n"
     ]
    }
   ],
   "source": [
    "pickle_file = r'datasets/notMNIST.pickle'\n",
    "\n",
    "with open(pickle_file, 'rb') as f:\n",
    "    save = pickle.load(f)\n",
    "    train_dataset = save['train_dataset']\n",
    "    train_labels = save['train_labels']\n",
    "    valid_dataset = save['valid_dataset']\n",
    "    valid_labels = save['valid_labels']\n",
    "    test_dataset = save['test_dataset']\n",
    "    test_labels = save['test_labels']\n",
    "    del save  # hint to help gc free up memory\n",
    "    print('Training set', train_dataset.shape, train_labels.shape)\n",
    "    print('Validation set', valid_dataset.shape, valid_labels.shape)\n",
    "    print('Test set', test_dataset.shape, test_labels.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([6, 6, 9, ..., 8, 4, 8], dtype=int32)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_labels"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 数据预处理\n",
    "将原始数据处理成模型可接受的形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training set (200000, 28, 28, 1) (200000, 10)\n",
      "Validation set (10000, 28, 28, 1) (10000, 10)\n",
      "Test set (10000, 28, 28, 1) (10000, 10)\n"
     ]
    }
   ],
   "source": [
    "image_size = 28\n",
    "num_labels = 10\n",
    "num_channels = 1 \n",
    "\n",
    "def reformat(dataset, labels):\n",
    "    dataset = dataset.reshape((-1, image_size, image_size, num_channels\n",
    "                              )).astype(np.float32)\n",
    "    labels = (np.arange(num_labels) == labels[:,None]).astype(np.float32)\n",
    "    return dataset, labels\n",
    "\n",
    "train_dataset, train_labels = reformat(train_dataset, train_labels)\n",
    "valid_dataset, valid_labels = reformat(valid_dataset, valid_labels)\n",
    "test_dataset, test_labels = reformat(test_dataset, test_labels)\n",
    "print('Training set', train_dataset.shape, train_labels.shape)\n",
    "print('Validation set', valid_dataset.shape, valid_labels.shape)\n",
    "print('Test set', test_dataset.shape, test_labels.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建CNN模型\n",
    "包含两个卷积层及一个全连接层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "learning_rate = 0.05\n",
    "batch_size = 16\n",
    "num_labels = 10\n",
    "img_size = 28\n",
    "img_channel = 1\n",
    "num_steps = 1001"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = tf.placeholder(tf.float32,\n",
    "                   shape=(None, img_size, img_size, img_channel))\n",
    "y = tf.placeholder(tf.float32, shape=(None, num_labels))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "weights = {\n",
    "    'conv1': tf.Variable(tf.truncated_normal([5, 5, 1, 16], stddev=0.1)),\n",
    "    'conv2': tf.Variable(tf.truncated_normal([5, 5, 16, 16], stddev=0.1)),\n",
    "    'full1': tf.Variable(tf.truncated_normal([7 * 7 * 16, 64], stddev=0.1)),\n",
    "    'out': tf.Variable(tf.truncated_normal([64, num_labels], stddev=0.1))\n",
    "}\n",
    "\n",
    "biases = {\n",
    "    'conv1': tf.Variable(tf.zeros([16])),\n",
    "    'conv2': tf.Variable(tf.constant(1.0, shape=[16])),\n",
    "    'full1': tf.Variable(tf.constant(1.0, shape=[64])),\n",
    "    'out': tf.Variable(tf.constant(1.0, shape=[num_labels]))\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def build_nets(X):\n",
    "    conv1 = tf.nn.conv2d(X, weights['conv1'], strides=[1, 2, 2, 1],\n",
    "                         padding='SAME')\n",
    "    conv1 = tf.nn.relu(tf.add(conv1, biases['conv1']))\n",
    "\n",
    "    conv2 = tf.nn.conv2d(conv1, weights['conv2'], strides=[1, 2, 2, 1],\n",
    "                         padding='SAME')\n",
    "    conv2 = tf.nn.relu(tf.add(conv2, biases['conv2']))\n",
    "\n",
    "    shape = weights['full1'].get_shape().as_list()[0]\n",
    "    full1 = tf.reshape(conv2, [-1, shape])\n",
    "    full1 = tf.matmul(full1, weights['full1'])\n",
    "    full1 = tf.nn.relu(tf.add(full1, biases['full1']))\n",
    "\n",
    "    out = tf.add(tf.matmul(full1, weights['out']), biases['out'])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Logging before flag parsing goes to stderr.\n",
      "W0925 11:07:18.697465 140286728304448 deprecation.py:323] From <ipython-input-10-536041b00ef1>:3: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "\n",
      "Future major versions of TensorFlow will allow gradients to flow\n",
      "into the labels input on backprop by default.\n",
      "\n",
      "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "logits = build_nets(X)\n",
    "loss = tf.reduce_mean(\n",
    "    tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))\n",
    "\n",
    "\n",
    "optimizer = tf.train.GradientDescentOptimizer(\n",
    "    learning_rate=learning_rate).minimize(loss)\n",
    "\n",
    "y_pred = tf.nn.softmax(logits)\n",
    "y_pred_correct = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y, 1))\n",
    "acc = tf.reduce_mean(tf.cast(y_pred_correct, tf.float32))\n",
    "\n",
    "init = tf.global_variables_initializer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized...\n",
      "--------------------------------------------------------------------------------\n",
      "Step 0: Minibatch Loss=3.00974440574646, Training Accuracy=0.125\n",
      "Validation Accuracy=0.10000000149011612\n",
      "--------------------------------------------------------------------------------\n",
      "Step 50: Minibatch Loss=1.8500714302062988, Training Accuracy=0.25\n",
      "Validation Accuracy=0.5400000214576721\n",
      "--------------------------------------------------------------------------------\n",
      "Step 100: Minibatch Loss=0.7997140884399414, Training Accuracy=0.6875\n",
      "Validation Accuracy=0.6937999725341797\n",
      "--------------------------------------------------------------------------------\n",
      "Step 150: Minibatch Loss=0.918752908706665, Training Accuracy=0.625\n",
      "Validation Accuracy=0.7712000012397766\n",
      "--------------------------------------------------------------------------------\n",
      "Step 200: Minibatch Loss=0.4610944986343384, Training Accuracy=0.875\n",
      "Validation Accuracy=0.7864000201225281\n",
      "--------------------------------------------------------------------------------\n",
      "Step 250: Minibatch Loss=0.4813616871833801, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.7889000177383423\n",
      "--------------------------------------------------------------------------------\n",
      "Step 300: Minibatch Loss=0.5399004817008972, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8090000152587891\n",
      "--------------------------------------------------------------------------------\n",
      "Step 350: Minibatch Loss=0.8405994176864624, Training Accuracy=0.625\n",
      "Validation Accuracy=0.7950999736785889\n",
      "--------------------------------------------------------------------------------\n",
      "Step 400: Minibatch Loss=1.462505578994751, Training Accuracy=0.625\n",
      "Validation Accuracy=0.8065999746322632\n",
      "--------------------------------------------------------------------------------\n",
      "Step 450: Minibatch Loss=0.3812478184700012, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.807200014591217\n",
      "--------------------------------------------------------------------------------\n",
      "Step 500: Minibatch Loss=0.9929307103157043, Training Accuracy=0.625\n",
      "Validation Accuracy=0.8126999735832214\n",
      "--------------------------------------------------------------------------------\n",
      "Step 550: Minibatch Loss=0.20051538944244385, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.814300000667572\n",
      "--------------------------------------------------------------------------------\n",
      "Step 600: Minibatch Loss=0.36569294333457947, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8159999847412109\n",
      "--------------------------------------------------------------------------------\n",
      "Step 650: Minibatch Loss=0.5052797198295593, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8266000151634216\n",
      "--------------------------------------------------------------------------------\n",
      "Step 700: Minibatch Loss=0.5826887488365173, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.828000009059906\n",
      "--------------------------------------------------------------------------------\n",
      "Step 750: Minibatch Loss=0.24919602274894714, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.8270000219345093\n",
      "--------------------------------------------------------------------------------\n",
      "Step 800: Minibatch Loss=0.4901612401008606, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8302000164985657\n",
      "--------------------------------------------------------------------------------\n",
      "Step 850: Minibatch Loss=0.7361021041870117, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8313000202178955\n",
      "--------------------------------------------------------------------------------\n",
      "Step 900: Minibatch Loss=0.5017877817153931, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8296999931335449\n",
      "--------------------------------------------------------------------------------\n",
      "Step 950: Minibatch Loss=0.3384958505630493, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8349999785423279\n",
      "--------------------------------------------------------------------------------\n",
      "Step 1000: Minibatch Loss=0.6342083215713501, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8248999714851379\n",
      "--------------------------------------------------------------------------------\n",
      "Optimization Finished!\n",
      "Test accuracy: 0.8928999900817871\n"
     ]
    }
   ],
   "source": [
    "with tf.Session() as sess:\n",
    "    sess.run(init)\n",
    "    print('Initialized...')\n",
    "    print('-'*80)\n",
    "    for i in range(num_steps):\n",
    "        offset = (i * batch_size) % (train_labels.shape[0] - batch_size)\n",
    "        batch_x = train_dataset[offset:offset + batch_size, :, :, :]\n",
    "        batch_y = train_labels[offset:offset + batch_size, :]\n",
    "        feed_dict = {X: batch_x, y: batch_y}\n",
    "        _, loss_val, train_acc = sess.run([optimizer, loss, acc],\n",
    "                                          feed_dict=feed_dict)\n",
    "\n",
    "        if i % 50 == 0:\n",
    "            print('Step {}: Minibatch Loss={}, Training Accuracy={}'.format(i, loss_val, train_acc))\n",
    "            val_acc = sess.run(acc,\n",
    "                               feed_dict={X: valid_dataset, y: valid_labels})\n",
    "            print('Validation Accuracy={}'.format(val_acc))\n",
    "            print('-'*80)\n",
    "    print('Optimization Finished!')\n",
    "    test_acc = sess.run(acc, feed_dict={X: test_dataset, y: test_labels})\n",
    "    print('Test accuracy: {}'.format(test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-17T14:20:32.628025Z",
     "start_time": "2020-04-17T14:20:32.622945Z"
    }
   },
   "source": [
    "# 优化模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 池化层+小步长\n",
    "将上述模型的步长减为 1，增加池化层\n",
    "- 模型测试精度增加 1%"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def build_nets(X):\n",
    "    conv1 = tf.nn.conv2d(X, weights['conv1'], strides=[1, 1, 1, 1],\n",
    "                         padding='SAME')\n",
    "    conv1 = tf.nn.relu(tf.add(conv1, biases['conv1']))\n",
    "    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],\n",
    "                          padding='SAME')\n",
    "\n",
    "    conv2 = tf.nn.conv2d(conv1, weights['conv2'], strides=[1, 1, 1, 1],\n",
    "                         padding='SAME')\n",
    "    conv2 = tf.nn.relu(tf.add(conv2, biases['conv2']))\n",
    "    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1],\n",
    "                          padding='SAME')\n",
    "\n",
    "    shape = weights['full1'].get_shape().as_list()[0]\n",
    "    full1 = tf.reshape(conv2, [-1, shape])\n",
    "    full1 = tf.matmul(full1, weights['full1'])\n",
    "    full1 = tf.nn.relu(tf.add(full1, biases['full1']))\n",
    "\n",
    "    out = tf.add(tf.matmul(full1, weights['out']), biases['out'])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "logits = build_nets(X)\n",
    "loss = tf.reduce_mean(\n",
    "    tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))\n",
    "\n",
    "\n",
    "optimizer = tf.train.GradientDescentOptimizer(\n",
    "    learning_rate=learning_rate).minimize(loss)\n",
    "\n",
    "y_pred = tf.nn.softmax(logits)\n",
    "y_pred_correct = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y, 1))\n",
    "acc = tf.reduce_mean(tf.cast(y_pred_correct, tf.float32))\n",
    "\n",
    "init = tf.global_variables_initializer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized...\n",
      "--------------------------------------------------------------------------------\n",
      "Step 0: Minibatch Loss=3.4627625942230225, Training Accuracy=0.0625\n",
      "Validation Accuracy=0.10000000149011612\n",
      "--------------------------------------------------------------------------------\n",
      "Step 50: Minibatch Loss=2.2258143424987793, Training Accuracy=0.125\n",
      "Validation Accuracy=0.3012999892234802\n",
      "--------------------------------------------------------------------------------\n",
      "Step 100: Minibatch Loss=1.1995855569839478, Training Accuracy=0.375\n",
      "Validation Accuracy=0.6039999723434448\n",
      "--------------------------------------------------------------------------------\n",
      "Step 150: Minibatch Loss=0.8889767527580261, Training Accuracy=0.75\n",
      "Validation Accuracy=0.767300009727478\n",
      "--------------------------------------------------------------------------------\n",
      "Step 200: Minibatch Loss=0.48465919494628906, Training Accuracy=0.875\n",
      "Validation Accuracy=0.7727000117301941\n",
      "--------------------------------------------------------------------------------\n",
      "Step 250: Minibatch Loss=0.6685712337493896, Training Accuracy=0.875\n",
      "Validation Accuracy=0.7967000007629395\n",
      "--------------------------------------------------------------------------------\n",
      "Step 300: Minibatch Loss=0.2359408438205719, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.805899977684021\n",
      "--------------------------------------------------------------------------------\n",
      "Step 350: Minibatch Loss=1.1302568912506104, Training Accuracy=0.6875\n",
      "Validation Accuracy=0.7961999773979187\n",
      "--------------------------------------------------------------------------------\n",
      "Step 400: Minibatch Loss=1.3927161693572998, Training Accuracy=0.5625\n",
      "Validation Accuracy=0.7802000045776367\n",
      "--------------------------------------------------------------------------------\n",
      "Step 450: Minibatch Loss=0.4781637489795685, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8219000101089478\n",
      "--------------------------------------------------------------------------------\n",
      "Step 500: Minibatch Loss=1.1723272800445557, Training Accuracy=0.625\n",
      "Validation Accuracy=0.8220999836921692\n",
      "--------------------------------------------------------------------------------\n",
      "Step 550: Minibatch Loss=0.22145673632621765, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.8163999915122986\n",
      "--------------------------------------------------------------------------------\n",
      "Step 600: Minibatch Loss=0.36087271571159363, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8256000280380249\n",
      "--------------------------------------------------------------------------------\n",
      "Step 650: Minibatch Loss=0.41324716806411743, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8359000086784363\n",
      "--------------------------------------------------------------------------------\n",
      "Step 700: Minibatch Loss=0.40102532505989075, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8384000062942505\n",
      "--------------------------------------------------------------------------------\n",
      "Step 750: Minibatch Loss=0.42055973410606384, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8385999798774719\n",
      "--------------------------------------------------------------------------------\n",
      "Step 800: Minibatch Loss=0.3077162206172943, Training Accuracy=0.9375\n",
      "Validation Accuracy=0.8406000137329102\n",
      "--------------------------------------------------------------------------------\n",
      "Step 850: Minibatch Loss=0.6074279546737671, Training Accuracy=0.8125\n",
      "Validation Accuracy=0.8393999934196472\n",
      "--------------------------------------------------------------------------------\n",
      "Step 900: Minibatch Loss=0.35880741477012634, Training Accuracy=0.875\n",
      "Validation Accuracy=0.8274000287055969\n",
      "--------------------------------------------------------------------------------\n",
      "Step 950: Minibatch Loss=0.5243210792541504, Training Accuracy=0.875\n",
      "Validation Accuracy=0.839900016784668\n",
      "--------------------------------------------------------------------------------\n",
      "Step 1000: Minibatch Loss=0.45970839262008667, Training Accuracy=0.875\n",
      "Validation Accuracy=0.845300018787384\n",
      "--------------------------------------------------------------------------------\n",
      "Optimization Finished!\n",
      "Test accuracy: 0.902400016784668\n"
     ]
    }
   ],
   "source": [
    "with tf.Session() as sess:\n",
    "    sess.run(init)\n",
    "    print('Initialized...')\n",
    "    print('-'*80)\n",
    "    for i in range(num_steps):\n",
    "        offset = (i * batch_size) % (train_labels.shape[0] - batch_size)\n",
    "        batch_x = train_dataset[offset:offset + batch_size, :, :, :]\n",
    "        batch_y = train_labels[offset:offset + batch_size, :]\n",
    "        feed_dict = {X: batch_x, y: batch_y}\n",
    "        _, loss_val, train_acc = sess.run([optimizer, loss, acc],\n",
    "                                          feed_dict=feed_dict)\n",
    "\n",
    "        if i % 50 == 0:\n",
    "            print('Step {}: Minibatch Loss={}, Training Accuracy={}'.format(i, loss_val, train_acc))\n",
    "            val_acc = sess.run(acc,\n",
    "                               feed_dict={X: valid_dataset, y: valid_labels})\n",
    "            print('Validation Accuracy={}'.format(val_acc))\n",
    "            print('-'*80)\n",
    "    print('Optimization Finished!')\n",
    "    test_acc = sess.run(acc, feed_dict={X: test_dataset, y: test_labels})\n",
    "    print('Test accuracy: {}'.format(test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 增加模型层数+`dropout`\n",
    "提升模型精度\n",
    "- 增加模型复杂度\n",
    "- 全连接层添加 dropout"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "def weight_variable(shape):\n",
    "    initial = tf.truncated_normal(shape, stddev=0.1)\n",
    "    return tf.Variable(initial)\n",
    "\n",
    "def bias_variable(shape):\n",
    "    initial = tf.constant(0.1, shape=shape)\n",
    "    return tf.Variable(initial)\n",
    "\n",
    "def conv2d(x, w):\n",
    "    return tf.nn.conv2d(x, w, strides=[1,1,1,1], padding='SAME')\n",
    "\n",
    "def max_pool_2x2(x):\n",
    "    return tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1, 2, 2, 1], padding='SAME')\n",
    "\n",
    "def conv_layer(input_, shape):\n",
    "    w = weight_variable(shape)\n",
    "    b = bias_variable([shape[3]])\n",
    "    return tf.nn.relu(conv2d(input_, w) + b)\n",
    "\n",
    "def full_layer(input_, size):\n",
    "    insize = int(input_.get_shape()[1])\n",
    "    w = weight_variable([insize, size])\n",
    "    b = bias_variable([size])\n",
    "    return tf.matmul(input_, w) + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [],
   "source": [
    "image_size = 28\n",
    "batch_size = 128\n",
    "num_labels = 10\n",
    "graph = tf.Graph()\n",
    "\n",
    "with graph.as_default():    \n",
    "    x = tf.placeholder(tf.float32, shape=[None,28,28,1])\n",
    "    y = tf.placeholder(tf.float32, shape=[None, 10])\n",
    "    keep_prob = tf.placeholder(tf.float32)\n",
    "    \n",
    "    conv1 = conv_layer(x, shape=[5, 5, 1, 16])\n",
    "    conv1_pool = max_pool_2x2(conv1)\n",
    "    \n",
    "    conv2 = conv_layer(conv1_pool, shape=[5,5,16,32])\n",
    "    conv2_pool = max_pool_2x2(conv2)\n",
    "    \n",
    "    conv3 = conv_layer(conv2_pool, shape=[5,5,32,64])\n",
    "    conv3_pool = max_pool_2x2(conv3)\n",
    "    \n",
    "    conv3_flat = tf.reshape(conv3_pool, [-1, 4*4*64])\n",
    "    conv3_drop = tf.nn.dropout(conv3_flat, keep_prob=keep_prob)\n",
    "    \n",
    "    full_1 = tf.nn.relu(full_layer(conv3_drop, 256))\n",
    "    full1_drop = tf.nn.dropout(full_1, keep_prob=keep_prob)\n",
    "    \n",
    "    y_pred = full_layer(full1_drop, 10)\n",
    "    \n",
    "    cross_entropy = tf.reduce_mean(\n",
    "        tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=y_pred))\n",
    "    \n",
    "    train_step = tf.train.AdamOptimizer(1e-3).minimize(cross_entropy)\n",
    "    \n",
    "    correct_predicetion = tf.equal(tf.argmax(y,1), tf.argmax(y_pred, 1))\n",
    "    accuracy = tf.reduce_mean(tf.cast(correct_predicetion, tf.float32))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized\n",
      "--------------------------------------------------------------------------------\n",
      "step 0:\n",
      "Minibatch loss: 4.313622951507568, accuracy: 0.125\n",
      "Validation accuracy: 0.10050000250339508\n",
      "--------------------------------------------------------------------------------\n",
      "step 500:\n",
      "Minibatch loss: 0.4675438106060028, accuracy: 0.8671875\n",
      "Validation accuracy: 0.8490999937057495\n",
      "--------------------------------------------------------------------------------\n",
      "step 1000:\n",
      "Minibatch loss: 0.5774704217910767, accuracy: 0.8671875\n",
      "Validation accuracy: 0.8683000206947327\n",
      "--------------------------------------------------------------------------------\n",
      "step 1500:\n",
      "Minibatch loss: 0.3340117633342743, accuracy: 0.9140625\n",
      "Validation accuracy: 0.8784000277519226\n",
      "--------------------------------------------------------------------------------\n",
      "step 2000:\n",
      "Minibatch loss: 0.32236236333847046, accuracy: 0.9140625\n",
      "Validation accuracy: 0.879800021648407\n",
      "--------------------------------------------------------------------------------\n",
      "step 2500:\n",
      "Minibatch loss: 0.29851555824279785, accuracy: 0.90625\n",
      "Validation accuracy: 0.8867999911308289\n",
      "--------------------------------------------------------------------------------\n",
      "step 3000:\n",
      "Minibatch loss: 0.4584856629371643, accuracy: 0.875\n",
      "Validation accuracy: 0.890500009059906\n",
      "--------------------------------------------------------------------------------\n",
      "Optimization Finished!\n",
      "Test accuracy: 0.9559000134468079\n"
     ]
    }
   ],
   "source": [
    "num_steps = 3001\n",
    "\n",
    "with tf.Session(graph=graph) as sess:\n",
    "    sess.run(tf.global_variables_initializer())\n",
    "    print(\"Initialized\")\n",
    "    print('-'*80)\n",
    "    for step in range(num_steps):\n",
    "        offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\n",
    "\n",
    "        batch_data = train_dataset[offset:(offset + batch_size)]\n",
    "        batch_labels = train_labels[offset:(offset + batch_size)]\n",
    "\n",
    "        sess.run(train_step,feed_dict={x: batch_data, \n",
    "                                       y: batch_labels,\n",
    "                                       keep_prob:0.5})\n",
    "        \n",
    "        if (step % 500 == 0):\n",
    "            loss, train_acc = sess.run([cross_entropy,accuracy],\n",
    "                                       feed_dict={x: batch_data, \n",
    "                                                  y :batch_labels,\n",
    "                                                  keep_prob:0.5})\n",
    "            print(\"step {}:\".format(step))\n",
    "            print(\"Minibatch loss: {}, accuracy: {}\".format(loss,train_acc))\n",
    "            val_acc = sess.run(accuracy,feed_dict={x: valid_dataset,\n",
    "                                                   y: valid_labels,\n",
    "                                                   keep_prob:0.5})\n",
    "            print(\"Validation accuracy: {}\".format(val_acc))\n",
    "            print('-'*80)\n",
    "\n",
    "    print('Optimization Finished!')\n",
    "    test_acc = sess.run(accuracy,feed_dict={x: test_dataset, \n",
    "                                            y: test_labels,\n",
    "                                            keep_prob:1.0})\n",
    "    print(\"Test accuracy: {}\".format(test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `LeNet5`+优化方法\n",
    "- 参数更新方法：AdamOptimizer 比 GradientDescentOptimizer 精度提升 3%"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = tf.placeholder(tf.float32, shape=[None, 28, 28, 1])\n",
    "y = tf.placeholder(tf.float32, shape=[None, 10])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [],
   "source": [
    "weights = {\n",
    "    'conv1': tf.Variable(tf.truncated_normal([5, 5, 1, 6], stddev=0.1)),\n",
    "    'conv2': tf.Variable(tf.truncated_normal([5, 5, 6, 16], stddev=0.1)),\n",
    "    'fc3': tf.Variable(tf.truncated_normal([5 * 5 * 16, 120], stddev=0.1)),\n",
    "    'fc4': tf.Variable(tf.truncated_normal([120, 84], stddev=0.1)),\n",
    "    'out': tf.Variable(tf.truncated_normal([84, 10], stddev=0.1))\n",
    "}\n",
    "\n",
    "biases = {\n",
    "    'conv1': tf.Variable(tf.constant(0.1, shape=[6])),\n",
    "    'conv2': tf.Variable(tf.constant(0.1, shape=[16])),\n",
    "    'fc3': tf.Variable(tf.constant(0.1, shape=[120])),\n",
    "    'fc4': tf.Variable(tf.constant(0.1, shape=[84])),\n",
    "    'out': tf.Variable(tf.constant(0.1, shape=[10]))\n",
    "}\n",
    "\n",
    "def build_nets(X, weights, biases):\n",
    "    z1 = tf.nn.conv2d(X, weights['conv1'], strides=[1, 1, 1, 1], padding='SAME')\n",
    "    y1 = tf.nn.relu(tf.add(z1, biases['conv1']))\n",
    "    y1 = tf.nn.max_pool2d(y1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')\n",
    "\n",
    "    z2 = tf.nn.conv2d(y1, weights['conv2'], strides=[1, 1, 1, 1], padding='VALID')\n",
    "    y2 = tf.nn.relu(tf.add(z2, biases['conv2']))\n",
    "    y2 = tf.nn.max_pool2d(y2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID')\n",
    "\n",
    "    y2 = tf.reshape(y2, [-1, weights['fc3'].get_shape().as_list()[0]])\n",
    "    z3 = tf.matmul(y2, weights['fc3'])\n",
    "    y3 = tf.nn.relu(tf.add(z3, biases['fc3']))\n",
    "\n",
    "    z4 = tf.matmul(y3, weights['fc4'])\n",
    "    y4 = tf.nn.relu(tf.add(z4, biases['fc4']))\n",
    "\n",
    "    out = tf.add(tf.matmul(y4, weights['out']), biases['out'])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {},
   "outputs": [],
   "source": [
    "logits = build_nets(X, weights, biases)\n",
    "y_pred = tf.nn.softmax(logits)\n",
    "\n",
    "loss = tf.reduce_mean(\n",
    "    tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=y))\n",
    "\n",
    "y_pred_correct = tf.equal(tf.argmax(y_pred, 1), tf.argmax(y, 1))\n",
    "acc = tf.reduce_mean(tf.cast(y_pred_correct, tf.float32))\n",
    "\n",
    "optimizer = tf.train.AdamOptimizer(1e-3).minimize(loss)\n",
    "\n",
    "init = tf.global_variables_initializer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 107,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized\n",
      "--------------------------------------------------------------------------------\n",
      "step 0:\n",
      "Minibatch loss: 2.2333452701568604, accuracy: 0.203125\n",
      "Validation accuracy: 0.14190000295639038\n",
      "--------------------------------------------------------------------------------\n",
      "step 200:\n",
      "Minibatch loss: 0.5003485679626465, accuracy: 0.84375\n",
      "Validation accuracy: 0.8303999900817871\n",
      "--------------------------------------------------------------------------------\n",
      "step 400:\n",
      "Minibatch loss: 0.45962750911712646, accuracy: 0.8359375\n",
      "Validation accuracy: 0.8550999760627747\n",
      "--------------------------------------------------------------------------------\n",
      "step 600:\n",
      "Minibatch loss: 0.40207576751708984, accuracy: 0.8671875\n",
      "Validation accuracy: 0.8690999746322632\n",
      "--------------------------------------------------------------------------------\n",
      "step 800:\n",
      "Minibatch loss: 0.6581531763076782, accuracy: 0.8203125\n",
      "Validation accuracy: 0.8799999952316284\n",
      "--------------------------------------------------------------------------------\n",
      "step 1000:\n",
      "Minibatch loss: 0.4802801311016083, accuracy: 0.8515625\n",
      "Validation accuracy: 0.8827999830245972\n",
      "--------------------------------------------------------------------------------\n",
      "step 1200:\n",
      "Minibatch loss: 0.5162304639816284, accuracy: 0.828125\n",
      "Validation accuracy: 0.8833000063896179\n",
      "--------------------------------------------------------------------------------\n",
      "step 1400:\n",
      "Minibatch loss: 0.3630096912384033, accuracy: 0.8828125\n",
      "Validation accuracy: 0.8844000101089478\n",
      "--------------------------------------------------------------------------------\n",
      "step 1600:\n",
      "Minibatch loss: 0.3749620020389557, accuracy: 0.8828125\n",
      "Validation accuracy: 0.8866000175476074\n",
      "--------------------------------------------------------------------------------\n",
      "step 1800:\n",
      "Minibatch loss: 0.46838340163230896, accuracy: 0.8671875\n",
      "Validation accuracy: 0.890999972820282\n",
      "--------------------------------------------------------------------------------\n",
      "step 2000:\n",
      "Minibatch loss: 0.2849087119102478, accuracy: 0.8984375\n",
      "Validation accuracy: 0.895799994468689\n",
      "--------------------------------------------------------------------------------\n",
      "Optimization Finished!\n",
      "Test accuracy: 0.9452999830245972\n"
     ]
    }
   ],
   "source": [
    "num_steps = 2001\n",
    "\n",
    "with tf.Session() as sess:\n",
    "    sess.run(init)\n",
    "    print(\"Initialized\")\n",
    "    print('-'*80)\n",
    "    for step in range(num_steps):\n",
    "        offset = (step * batch_size) % (train_labels.shape[0] - batch_size)\n",
    "\n",
    "        batch_data = train_dataset[offset:(offset + batch_size)]\n",
    "        batch_labels = train_labels[offset:(offset + batch_size)]\n",
    "\n",
    "        sess.run(optimizer,feed_dict={X: batch_data, y: batch_labels})\n",
    "        \n",
    "        if (step % 200 == 0):\n",
    "            loss_val, train_acc = sess.run([loss,acc],\n",
    "                                       feed_dict={X: batch_data, \n",
    "                                                  y :batch_labels})\n",
    "            print(\"step {}:\".format(step))\n",
    "            print(\"Minibatch loss: {}, accuracy: {}\".format(loss_val,train_acc))\n",
    "            val_acc = sess.run(acc,feed_dict={X: valid_dataset,\n",
    "                                                   y: valid_labels})\n",
    "            print(\"Validation accuracy: {}\".format(val_acc))\n",
    "            print('-'*80)\n",
    "\n",
    "    print('Optimization Finished!')\n",
    "    test_acc = sess.run(acc,feed_dict={X: test_dataset,y: test_labels})\n",
    "    print(\"Test accuracy: {}\".format(test_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `Keras Callbacks`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CNN 结构如下：\n",
    "            \n",
    "`In -> [[Conv2D->relu]*2 -> MaxPool2D -> Dropout]*2 -> Flatten -> Dense -> Dropout -> Out`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.image as mpimg\n",
    "import seaborn as sns\n",
    "%matplotlib inline\n",
    "\n",
    "np.random.seed(2)\n",
    "\n",
    "from sklearn.metrics import confusion_matrix\n",
    "import itertools\n",
    "\n",
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D\n",
    "from keras.optimizers import RMSprop\n",
    "from keras.preprocessing.image import ImageDataGenerator\n",
    "from keras.callbacks import ReduceLROnPlateau\n",
    "\n",
    "sns.set(style='white', context='notebook', palette='deep')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "g = plt.imshow(train_dataset[11][:,:,0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential()\n",
    "\n",
    "model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', \n",
    "                 activation ='relu', input_shape = (28,28,1)))\n",
    "model.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', \n",
    "                 activation ='relu'))\n",
    "model.add(MaxPool2D(pool_size=(2,2)))\n",
    "model.add(Dropout(0.25))\n",
    "\n",
    "\n",
    "model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', \n",
    "                 activation ='relu'))\n",
    "model.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', \n",
    "                 activation ='relu'))\n",
    "model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))\n",
    "model.add(Dropout(0.25))\n",
    "\n",
    "\n",
    "model.add(Flatten())\n",
    "model.add(Dense(256, activation = \"relu\"))\n",
    "model.add(Dropout(0.5))\n",
    "model.add(Dense(10, activation = \"softmax\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(optimizer = optimizer , loss = \"categorical_crossentropy\", metrics=[\"accuracy\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [],
   "source": [
    "learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc', \n",
    "                                            patience=3, \n",
    "                                            verbose=1, \n",
    "                                            factor=0.5, \n",
    "                                            min_lr=0.00001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [],
   "source": [
    "epochs = 5\n",
    "batch_size = 128"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 200000 samples, validate on 10000 samples\n",
      "Epoch 1/5\n",
      " - 694s - loss: 0.4321 - acc: 0.8719 - val_loss: 0.3204 - val_acc: 0.9082\n",
      "Epoch 2/5\n",
      " - 693s - loss: 0.3372 - acc: 0.9005 - val_loss: 0.3156 - val_acc: 0.9137\n",
      "Epoch 3/5\n",
      " - 692s - loss: 0.3289 - acc: 0.9040 - val_loss: 0.3031 - val_acc: 0.9134\n",
      "Epoch 4/5\n",
      " - 689s - loss: 0.3292 - acc: 0.9036 - val_loss: 0.3039 - val_acc: 0.9145\n",
      "Epoch 5/5\n",
      " - 693s - loss: 0.3332 - acc: 0.9035 - val_loss: 0.3109 - val_acc: 0.9126\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(train_dataset, train_labels, batch_size = batch_size, epochs = epochs,\n",
    "                    validation_data = (valid_dataset, valid_labels), verbose = 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(2,1)\n",
    "ax[0].plot(history.history['loss'], color='b', label=\"Training loss\")\n",
    "ax[0].plot(history.history['val_loss'], color='r', label=\"validation loss\",axes =ax[0])\n",
    "legend = ax[0].legend(loc='best', shadow=True)\n",
    "\n",
    "ax[1].plot(history.history['acc'], color='b', label=\"Training accuracy\")\n",
    "ax[1].plot(history.history['val_acc'], color='r',label=\"Validation accuracy\")\n",
    "legend = ax[1].legend(loc='best', shadow=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def plot_confusion_matrix(cm, classes,\n",
    "                          normalize=False,\n",
    "                          title='Confusion matrix',\n",
    "                          cmap=plt.cm.Blues):\n",
    "    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
    "    plt.title(title)\n",
    "    plt.colorbar()\n",
    "    tick_marks = np.arange(len(classes))\n",
    "    plt.xticks(tick_marks, classes, rotation=45)\n",
    "    plt.yticks(tick_marks, classes)\n",
    "\n",
    "    if normalize:\n",
    "        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
    "\n",
    "    thresh = cm.max() / 2.\n",
    "    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
    "        plt.text(j, i, cm[i, j],\n",
    "                 horizontalalignment=\"center\",\n",
    "                 color=\"white\" if cm[i, j] > thresh else \"black\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.ylabel('True label')\n",
    "    plt.xlabel('Predicted label')\n",
    "    \n",
    "Y_pred = model.predict(test_dataset)\n",
    "Y_pred_classes = np.argmax(Y_pred,axis = 1) \n",
    "Y_true = np.argmax(test_labels,axis = 1) \n",
    "confusion_mtx = confusion_matrix(Y_true, Y_pred_classes) \n",
    "plot_confusion_matrix(confusion_mtx, classes = range(10))     "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test accuracy: 0.911899983882904\n"
     ]
    }
   ],
   "source": [
    "y_pred_correct = tf.equal(tf.argmax(y_pred, 1), tf.argmax(test_labels, 1))\n",
    "acc = tf.reduce_mean(tf.cast(y_pred_correct, tf.float32))\n",
    "print(\"Test accuracy: {}\".format(test_acc))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.7"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "198.667px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
